package com.wallace.downloadserver.util.myEncrypt

import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.security.InvalidKeyException
import java.security.KeyFactory
import java.security.KeyPairGenerator
import java.security.NoSuchAlgorithmException
import java.security.interfaces.RSAPrivateKey
import java.security.interfaces.RSAPublicKey
import java.security.spec.InvalidKeySpecException
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
import javax.crypto.BadPaddingException
import javax.crypto.Cipher
import javax.crypto.IllegalBlockSizeException
import javax.crypto.NoSuchPaddingException

/**
 * 注意：RSA的公私密钥有格式的区分：PKCS#1 和 PKCS8
 * PKCS1 是php使用的，PKCS8为java和客户端使用。切记
 */
object RSAUtil {
    private const val RSA_ALGORITHM = "RSA"
    private const val RSA_TRANSFORM = "RSA/ECB/PKCS1Padding"

    /**
     * RSA最大加密明文大小
     */
    private const val MAX_ENCRYPT_BLOCK = 117

    /**
     * RSA最大解密密文大小
     */
    private const val MAX_DECRYPT_BLOCK = 128

    /**
     * 密钥长度，RSA算法的默认密钥长度是1024
     * 密钥长度必须是64的倍数，在512到65536位之间
     */
    private const val KEY_SIZE = 1024
    private val log: Logger = LoggerFactory.getLogger(RSAUtil::class.java)

    /**
     * 生成密钥对
     * @return 密钥对对象
     * @throws NoSuchAlgorithmException
     */
    @Throws(NoSuchAlgorithmException::class)
    fun createKeys(): RSAKey {
        //KeyPairGenerator用于生成公钥和私钥对。密钥对生成器是使用 getInstance 工厂方法
        val keyPairGenerator =
                KeyPairGenerator.getInstance(RSA_ALGORITHM)
        keyPairGenerator.initialize(KEY_SIZE)
        val keyPair = keyPairGenerator.generateKeyPair()
        val privateKey = keyPair.private as RSAPrivateKey
        val publicKey = keyPair.public as RSAPublicKey
        return RSAKey(publicKey, privateKey)
    }

    /**
     * 私钥加密
     * @param data 待加密数据
     * @param key 公钥
     * @return byte[] 加密后的数据
     */
    @Throws(Exception::class)
    fun encryptByPrivateKey(data: ByteArray?, key: ByteArray?): ByteArray {
        //取得私钥
        val pkcs8KeySpec = PKCS8EncodedKeySpec(key)
        val keyFactory = KeyFactory.getInstance(RSA_ALGORITHM)
        //生成私钥
        val privateKey = keyFactory.generatePrivate(pkcs8KeySpec)
        //数据加密
        val cipher: Cipher = Cipher.getInstance(RSA_TRANSFORM)
        cipher.init(Cipher.ENCRYPT_MODE, privateKey)
        return cipher.doFinal(data)
    }

    /**
     * 公钥加密
     * @param data 待加密数据
     * @param key 公钥
     * @return byte[] 加密后的数据
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws InvalidKeyException
     */
    @Throws(
            NoSuchAlgorithmException::class,
            InvalidKeySpecException::class,
            NoSuchPaddingException::class,
            BadPaddingException::class,
            IllegalBlockSizeException::class,
            InvalidKeyException::class
    )
    fun encryptByPublicKey(data: ByteArray, key: ByteArray): ByteArray {
        //实例化密钥工厂
        val keyFactory = KeyFactory.getInstance(RSA_ALGORITHM)
        //初始化公钥,根据给定的编码密钥创建一个新的 X509EncodedKeySpec。
        val x509EncodedKeySpec = X509EncodedKeySpec(key)
        val publicKey = keyFactory.generatePublic(x509EncodedKeySpec)
        //数据加密
        val cipher: Cipher = Cipher.getInstance(RSA_TRANSFORM)
        cipher.init(Cipher.ENCRYPT_MODE, publicKey)
        return cipher.doFinal(data)
    }

    /**
     * 私钥解密
     * @param data 待解密数据
     * @param key 密钥
     * @return byte[] 解密数据
     */
    @Throws(Exception::class)
    fun decryptByPrivateKey(data: ByteArray?, key: ByteArray?): ByteArray {
        //取得私钥
        val pkcs8KeySpec = PKCS8EncodedKeySpec(key)
        val keyFactory = KeyFactory.getInstance(RSA_ALGORITHM)
        //生成私钥
        val privateKey = keyFactory.generatePrivate(pkcs8KeySpec)
        //数据解密
        val cipher: Cipher = Cipher.getInstance(RSA_TRANSFORM)
        cipher.init(Cipher.DECRYPT_MODE, privateKey)
        return cipher.doFinal(data)
    }

    /**
     * 私钥（数据分段）解密
     * @param data 密文
     * @param privateKeyByteArray ByteArray类型的私钥
     * @return ByteArray 明文
     */
    fun decryptByPrivateKey1(data: ByteArray, privateKeyByteArray: ByteArray?): ByteArray? {
        var decryptedData: ByteArray? = null
        //取得私钥
        val pkcs8KeySpec = PKCS8EncodedKeySpec(privateKeyByteArray)
        try {
            val keyFactory = KeyFactory.getInstance(RSA_ALGORITHM)
            //生成私钥
            val privateKey = keyFactory.generatePrivate(pkcs8KeySpec)
            // 解密数据，分段解密
            val cipher = Cipher.getInstance(RSA_TRANSFORM)
            cipher.init(Cipher.DECRYPT_MODE, privateKey)
            val inputLength = data.size
            val out = ByteArrayOutputStream()
            var offset = 0
            var cache: ByteArray
            var i = 0
            while (inputLength - offset > 0) {
                cache = if (inputLength - offset > MAX_DECRYPT_BLOCK) {
                    cipher.doFinal(data, offset, MAX_DECRYPT_BLOCK)
                } else {
                    cipher.doFinal(data, offset, inputLength - offset)
                }
                out.write(cache)
                i++
                offset = i * MAX_DECRYPT_BLOCK
            }
            decryptedData = out.toByteArray()
            out.close()
        } catch (e: NoSuchAlgorithmException) {
            log.error("$e")
        } catch (e: InvalidKeySpecException) {
            log.error("$e")
        } catch (e: NoSuchPaddingException) {
            log.error("$e")
        } catch (e: IOException) {
            log.error("$e")
        } finally {
            return decryptedData
        }
    }

    /**
     * 公钥解密
     * @param data 待解密数据
     * @param key 密钥
     * @return byte[] 解密数据
     */
    @Throws(Exception::class)
    fun decryptByPublicKey(data: ByteArray?, key: ByteArray?): ByteArray {
        //实例化密钥工厂
        val keyFactory = KeyFactory.getInstance(RSA_ALGORITHM)
        //初始化公钥
        //密钥材料转换
        val x509KeySpec = X509EncodedKeySpec(key)
        //产生公钥
        val pubKey = keyFactory.generatePublic(x509KeySpec)
        //数据解密
        val cipher: Cipher = Cipher.getInstance(RSA_TRANSFORM)
        cipher.init(Cipher.DECRYPT_MODE, pubKey)
        return cipher.doFinal(data)
    }
}